{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<center>\n",
    "<img src=\"../../img/ods_stickers.jpg\">\n",
    "## Открытый курс по машинному обучению. Сессия № 2\n",
    "\n",
    "### <center> Автор материала: Бирюкова Валентина (@myltykritik)\n",
    "\n",
    "## Индивидуальный проект по анализу данных"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Описание и ценность решаемой задачи\n",
    "С давних времен человечество страдало от недоедания и голода, но недавно столкнулось с новой проблемой — ожирением. И пока на одной части света люди продолжают умирать от истощения, другая часть населения планеты умирает от ожирения и болезней, связанных с ним. И уже с начала 21 века большинство населения проживает в странах, где от последствий избыточного веса умирает больше людей, чем от последствий болезней, связанных с низкой массой тела (1).\n",
    "\n",
    "Что такое ожирение? На сайте Всемирной Организации Здравоохранения (ВОЗ) приводится следующее определение (2): «Избыточный вес и ожирение – результат формирования аномальных или чрезмерных жировых отложений, которые могут наносить вред здоровью».\n",
    "\n",
    "Наиболее распространенным способом измерения избыточного веса и ожирения является подсчет Индекса массы тела (ИМТ). Это универсальный способ подсчета значения для мужчин и женщин с некоторыми особенностями для детей, которые в рамках этой работы нам не очень интересны.\n",
    "\n",
    "Формула выглядит следующим образом и измеряется в кг/м$^2$: Масса тела в килограммах/Рост в метрах$^2$\n",
    "\n",
    "Считается, что диагноз избыточный вес ставится при ИМТ большем 25 кг/м$^2$, а ожирение — при 30 кг/м$^2$.\n",
    "\n",
    "Чем же страшны жировые отложения для человека?\n",
    "\n",
    "Излишний вес является одним из главных факторов развития ряда болезней, включая рак, диабет и сердечно-сосудистые заболевания (3). Ранее считалась, что проблемы лишнего веса и ожирения преследуют людей только в богатых, экономически развитых странах с высоким уровнем дохода. Но в последнее время это явление получило огромное распространение в странах со средним и даже низким уровнем доходов. Особенно подвержены проблеме жители городов.\n",
    "\n",
    "Что вызывает избыточный вес и ожирение?\n",
    "\n",
    "Исследования показывают, что причиной избыточного веса является энергетический дисбаланс в организме. Люди потребляют с едой больше питательных веществ, чем им необходимо для жизнедеятельности. ВОЗ отмечает некоторые тенденции (4):\n",
    "\n",
    "* Люди, в целом, начали потреблять больше калорий;\n",
    "* Изменилась физическая активность населения — сидячая работа, развитие транспорта и урбанизация.\n",
    "\n",
    "1 http://www.who.int/mediacentre/factsheets/fs311/ru/ \n",
    "\n",
    "2 Там же.\n",
    "\n",
    "3 http://www.who.int/topics/obesity/ru/ \n",
    "\n",
    "4 http://www.who.int/mediacentre/factsheets/fs311/ru/ \n",
    "\n",
    "\n",
    "Население с высокой долей людей с избыточным весом - это нездоровое население, склонное к большому ряду болезней, которое быстро перестает работать и переходит на пособия по болезни. Поэтому для государства важно отслеживать этот аспект злоровья людей и пытаться направить их в нужное русло.\n",
    "\n",
    "Ожирение пытаются предсказывать разными способами: анализируя физическую активность, пищевое поведение, изменение химического состава крови на протяжении времени и другими.\n",
    "\n",
    "Но в Америке задались вопросом, можно ли предсказывать ожирение людей по их стилю жизни? Они проводили опросы среди домохозяйств с целью обработать данные и выявить какие-то закономерности. Я сейчас этим и займусь."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Сбор данных и описание признаков\n",
    "\n",
    "Для анализа я выбрала данные https://www.bls.gov/tus/ehmintcodebk1416.pdf о поведении некоторых американских домохозяйств, так как, возможно, финансовое поведение людей, их уровень богатсва, а также потребление некоторых продуктов сможет рассказать нам что-то о жизни людей и успешно прогнозировать находятся ли они в зоне риска или нет.\n",
    "\n",
    "Целевого признака ожирения в данных нет, но он легко восстанавливается из bmi (ИМТ). Классически советуют ожирение считать с имт равным 30, но, как мне кажется, излишний вес (имт > 25) уже много. Поэтому я решила взять порог ожирения имт > 28.\n",
    "\n",
    "Описание переменных, которые будем использовать:\n",
    "- eeincome1 — нужно ответить на вопрос, был ли ваш уровень дохода до вычита налогов в прошлом году больше 185% от уровня бедности. 1 — да, 2 — нет, 3 — в точности;\n",
    "- erbmi — посчитанный имт;\n",
    "- ertpreat — суммарное кол-во времени, проведенное за едой и питьем;\n",
    "- ertseat — суммарное кол-во времени, проведенное за перекусами;\n",
    "- eudietsoda — признак, показывающий, какую содовую пьет опрашиваемый: 1 — диетическую, 2 — с сахаром  или 3 — обе;\n",
    "- eudrink — были ли еще какие напитки кроме воды;\n",
    "- eueat — были ли случаи вчера, когда вы ели что-то в процессе занятия другим делом;\n",
    "- euexercise — была ли у вас физическая нагрузка (зал, бассейн, бег, велосипед) в течении последних 30 дней;\n",
    "- euexfreq — сколько раз у вас была эта нагрузка;\n",
    "- eufastfd — покупали ли вы в течение прошлых 7 дней приготовленную пищу или фастфуд;\n",
    "- eufastfdfrq — сколько раз вы покупали фастфуд;\n",
    "- euffyday — покупали ли вы фастфуд вчера;\n",
    "- eufdsit — было ли у вас достаточно еды в последние 30 дней: 1 — да, 2 — иногда нечего было есть, 3 — часто нечего было есть;\n",
    "- eusnap — участвовало ли ваше домохозяйство в продовольственных программах помощи в течение последнего месяца;\n",
    "- eugenhth — каково, на ваш взгляд, ваше здоровье: 1 — превосходно, 2 — очень хорошее, 3 — хорошее, 4 — нормальное, 5 — плохое;\n",
    "- eugroshp — являетесь ли вы тем человеком, кто постоянно покупает продукты для семьи: 1 — да, 2 — нет, 3 — разделяю обязанность с кем-то;\n",
    "- euhgt — рост в дюймах;\n",
    "- eumeat — ели ли вы за последние 7 дней мясо, птицу или морепродукты;\n",
    "- eumilk — пили ли вы сырое молоко за последние 7 дней;\n",
    "- euprpmel — являетесь ли вы тем человеком, кто постоянно готовит для семьи: 1 — да, 2 — нет, 3 — разделяю обязанность с кем-то;\n",
    "- eusoda — пьете ли вы содовую, рутбир или имбирный эль;\n",
    "- eustores — где вы совершаете большинство покупок: 1 — продуктовый магазин; 2 — супермаркет, 3 — склад, 4 — магазин, 5 — другое место;\n",
    "- eustreason — в чем основная причина, что вы закупаетесь там: 1 — цена; 2 — местоположение, 3 — качество продуктов, 4 — выбор продукции, 5 — сервис, 6 — другое;\n",
    "- eutherm — используете ли вы кулинарный термметр в приготовлении еды;\n",
    "- euwgt — вес в фунтах;\n",
    "- euwic — участвовало ли ваше домохозяйство в программах помощи в течение последнего месяца.\n",
    "\n",
    "Описание переменных, которые являются вспомогательными и их не нужно использовать в модели:\n",
    "- tucaseid — идентификатор каждого домохозяйства;\n",
    "- tulineno — признак, который всегда принимает значение 1;\n",
    "- erhhch — про связь двух исследований CPS и ATUS;\n",
    "- erincome — излишне детальное разбиение по уровню доходов, на 90% коррелирует с eeincome1;\n",
    "- eufinlwgt — производная веса;\n",
    "- erspemch — про связь двух исследований;\n",
    "- ethgt и etwgt — метки, показывающие, что экстримальные значения роста и веса были приведены к минимуму и максимуму. На первый взгляд, пропуски и выбросы лучше иначе заменять, так что уберем эти метки;\n",
    "- euinclvl — метка для  EUINCOME1 и EUINCOME2;\n",
    "- euincome2 — еще один показатель бедности по порогу 130% от уровня бедности;\n",
    "- exincome1 флаг для eeincome1.\n",
    "\n",
    "\n",
    "\n",
    "Варианты ответов, если не указано иное: 1 — да, 2 — нет.\n",
    "\n",
    "Обработка пропусков: -1 — пусто, -2 — не знает, -3 — отказались отвечать."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "План работы:\n",
    "* Описание и ценность решаемой задачи (выше)\n",
    "* Описание набора данных и признаков (выше)\n",
    "* Первичный анализ данных\n",
    "* Первичный визуальный анализ данных\n",
    "* Инсайты, найденные зависимости\n",
    "* Выбор метрики\n",
    "* Выбор модели\n",
    "* Предобработка данных\n",
    "* Кросс-валидация и настройка гиперпараметров модели\n",
    "* Создание новых признаков и описание этого процесса\n",
    "* Прогноз для тестовой или отложенной выборке\n",
    "* Выводы"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import seaborn as sns\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "\n",
    "sns.set_style('whitegrid')\n",
    "pd.set_option('display.max_columns', 500)\n",
    "pd.set_option('display.max_rows', 500)\n",
    "plt.rcParams[\"axes.labelsize\"] = 15\n",
    "sns.set(style = 'white', font_scale=1.5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = pd.read_csv(\"../../ehresp_2014.csv\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Первичный анализ данных\n",
    "### Первичный визуальный анализ данных\n",
    "### Инсайты, найденные зависимости\n",
    "\n",
    "Далее идет логичная обработка данных, совмещающая сразу несколько пунктов"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data.describe()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Удалим ненужные столбцы и посмотрим, сколько значений -1 (пропуски без ответов) в наших данных."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = data.drop(['tucaseid', 'tulineno', 'erhhch', 'erincome', 'erspemch', 'ethgt', 'etwgt', 'euinclvl', \n",
    "                 'euincome2', 'exincome1', 'eufinlwgt'], axis=1)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sns.set(font_scale=2)\n",
    "fig, ax = plt.subplots(figsize=(20,10)) \n",
    "sns.heatmap(data.values == -1, yticklabels=False, cbar=False, cmap='PiYG_r',\\\n",
    "           xticklabels=data.columns) #Spectral"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В некоторых столбцах так много пропусков, что гораздо целесообразнее удалить их, а не пытаться восстанавливать. Мне кажется, что пропусков много в таких не самых важных вопросах потому, что люди при опросах просто не доходили до них."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = data.drop(['eudietsoda', #Люди не смогли ответить про то, какую содовую пьют\n",
    "                  'euexfreq', #Не смогли ответить, сколько раз занимались физ.упражнениями\n",
    "                  'eufastfdfrq', 'euffyday', #Не ответили на несколько вопросов про фастфуд\n",
    "                  'eumeat', 'eumilk', #Не ответили про потребление мяся и морепродуктов\n",
    "                 'eusoda', #Опять газировка\n",
    "                  'eustores', 'eustreason', #Вопросы про выбор магазина для покупок\n",
    "                  'eutherm',  #Возможно, не все поняли вопросы про кулинарный термометр\n",
    "                  'euwic'], axis = 1) #Вопрос про участиев опросах\n",
    "\n",
    "sns.set(font_scale=2)\n",
    "fig, ax = plt.subplots(figsize=(20,10)) \n",
    "sns.heatmap(data.values == -1.0, yticklabels=False,cbar=False,cmap='PiYG_r',\\\n",
    "           xticklabels=data.columns) #Spectral"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Теперь посмотрим на все остальные пропуски (-1, -2, -3) и возможные ошибки (-4, -5). Видим, что пропуски в имт обусловлены пропусками в весе или росте. Есть еще какие-то единичные пробелы. В целом, данных достаточно и строки с пропусками мы можем просто удалить, так как сложно предположить, что отвечали люди. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = data.replace([-1, -2, -3, -4, -5], np.nan)\n",
    "sns.set(font_scale=2)\n",
    "fig, ax = plt.subplots(figsize=(20,10)) \n",
    "sns.heatmap(data.isnull(),yticklabels=False,cbar=False,cmap='PiYG_r') #Spectral"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# удаляем\n",
    "data = data.dropna()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Приведем рост и вес к метрической системе мер (к метрам и килограмам). И посмотрим на данные."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data['euwgt'] *= 0.453592\n",
    "data['euhgt'] *= 0.0254\n",
    "\n",
    "data.describe()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Посмотрим на данные поближе. Похоже, есть какие-то выбросы в количествах минут первичной и вторичной еды (перекусы), есть слишком большие имт. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sns.set(style = 'whitegrid', font_scale=1.5)\n",
    "c =  sns.color_palette('PiYG')[0]\n",
    "fig, ax = plt.subplots(figsize=(15, 8))\n",
    "plt.plot(data['ertpreat'], 'o', color = c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "Заменим выбросы, что больше 350, на медианные значения.\n",
    "\n",
    "Построим дальше такой график для ertseat."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data.loc[data.ertpreat >= 350, 'ertpreat'] = round(data.ertpreat.median(),0)\n",
    "fig, ax = plt.subplots(figsize=(15, 8))\n",
    "plt.plot(data['ertseat'], 'o', color = c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "Заменим значения, большие 800, на медиану.\n",
    "\n",
    "Теперь посмотрим на рост и вес. Заметим, что особых выбросов нет, но люди с аномально маленьким и больши весом заменялись на граничные значения 44 кг и 154 кг или 1.95 м и 1.42 м"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data.loc[data.ertseat >= 800, 'ertseat'] = round(data.ertseat.median(),0)\n",
    "\n",
    "fig, ax = plt.subplots(figsize=(15, 8))\n",
    "plt.plot(data['euhgt'], 'o', color = c)\n",
    "\n",
    "fig, ax = plt.subplots(figsize=(15, 8))\n",
    "plt.plot(data['euwgt'], 'o', color = c)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Посмотрим на распределение роста у людей с аномальным весом и на распределение веса у людей с аномальным ростом."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data['euwgt_bin'] = data['euwgt'].apply(lambda x: 2 if x>154 else (0 if x<45 else 1) )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sns.set(style = 'whitegrid', font_scale=1.5)\n",
    "c1 =  sns.color_palette('PiYG')\n",
    "fig, ax = plt.subplots(figsize=(15, 8))\n",
    "sns.violinplot(x = 'euwgt_bin', y = 'euhgt', data = data, inner='box', palette=c1, ax = ax)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Заменим вес 0 группы на значение веса первой квантили людей, а 2 группы - последней квантили, так как заменять просто на среднее или медианное значение не стоит, так как видна большая разница."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(data.euwgt.quantile(0.10, 'midpoint'))\n",
    "print(data.euwgt.quantile(0.90, 'midpoint'))\n",
    "\n",
    "data.loc[(data.euwgt_bin == 0), 'euwgt'] = data.euwgt.quantile(0.10, 'midpoint')\n",
    "data.loc[(data.euwgt_bin == 2), 'euwgt'] = data.euwgt.quantile(0.90, 'midpoint')\n",
    "data.describe()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Повторим то же самое с ростом."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data['euhgt_bin'] = data['euhgt'].apply(lambda x: 2 if x>1.95 else (0 if x<1.43 else 1) )\n",
    "sns.set(style = 'whitegrid', font_scale=1.5)\n",
    "fig, ax = plt.subplots(figsize=(15, 8))\n",
    "sns.violinplot(x = 'euhgt_bin', y = 'euwgt', data = data, inner='box', palette=c1, ax = ax)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(data.euhgt.quantile(0.10, 'midpoint'))\n",
    "print(data.euhgt.quantile(0.90, 'midpoint'))\n",
    "\n",
    "data.loc[(data.euhgt_bin == 0), 'euhgt'] = data.euhgt.quantile(0.10, 'midpoint')\n",
    "data.loc[(data.euhgt_bin == 2), 'euhgt'] = data.euhgt.quantile(0.90, 'midpoint')\n",
    "data.describe()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "С аномалиями разобрались, рост и вес привели в порядок, но надо теперь пересчитать имт, так как с изменением роста или веса он тоже должен поменяться.\n",
    "И посмотрим на аномальные имт."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data['bmi'] = data['euwgt']/(data['euhgt'])**2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# data.loc[(data.euwgt <= 45)|(data.euwgt >= 154), 'euwgt'] = round(data.euwgt.mean(),0)\n",
    "# data1 = data[(data['euwgt']>=154)|(data['euwgt']<=45)]\n",
    "data1 = data[(data['bmi']>=55)|(data['bmi']<=16)]\n",
    "sns.set(style = 'whitegrid', font_scale=1.5)\n",
    "fig, ax = plt.subplots(figsize=(15, 8))\n",
    "plt.plot(data1['bmi'], 'o', color = c)\n",
    "plt.title('Высокий и низкий имт')\n",
    "plt.xlabel('Люди')\n",
    "plt.ylabel('Имт')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В целом, аномальные значения выглядят логично: большой рост и маленький, но не критичный вес. И наоборот, очень большой вес при среднем росте. Можно допустить, что это нормальные значения и ничего с ними не делать.\n",
    "\n",
    "Теперь можно сделать целевой признак ожирения. Повторюсь, назначать ожирение с имт равным 30 — слишком много, поэтому я опущу границу до 28."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data['obese'] = data['bmi'].apply(lambda x: int(x >= 28))\n",
    "data['obese'].value_counts()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ds = data.corr()\n",
    "sns.set(font_scale=2)\n",
    "fig, ax = plt.subplots(figsize=(20,10)) \n",
    "sns.heatmap(data=ds, cmap='PiYG_r', annot=True, fmt = '.1f', center=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Уберем bmi, так как на его основе рассчитывался obese, уберем erbmi, так как это тот же bmi, уберем euwgt, так как на его основе рассчитывается bmi. А euhgt оставим, так как он вообще не коррелирует с ожирением. Еще удалим вспомогательные euwgt_bin и euhgt_bin.\n",
    "\n",
    "Заодно посмотрим корреляционную матрицу с ранговой корреляцией Спирмена."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = data.drop(['bmi',  'erbmi', 'euwgt', 'euwgt_bin', 'euhgt_bin'], axis = 1)\n",
    "ds = data.corr()\n",
    "sns.set(font_scale=2)\n",
    "fig, ax = plt.subplots(figsize=(20,10)) \n",
    "sns.heatmap(data=ds, cmap='PiYG_r', annot=True, fmt = '.1f', center=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ds = data.corr(method='spearman')\n",
    "sns.set(font_scale=2)\n",
    "fig, ax = plt.subplots(figsize=(20,10)) \n",
    "sns.heatmap(data=ds, cmap='PiYG_r', annot=True, fmt = '.1f', center=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "Видим, что появилась неожиданная сильная корреляция ertseat и eueat. \n",
    "\n",
    "Оказывается, ertseat = 0 тогда, когда eueat = 2. Значит, eueat излишний признак."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sns.set(style = 'whitegrid', font_scale=1.5)\n",
    "c1 =  sns.color_palette('PiYG')\n",
    "fig, ax = plt.subplots(figsize=(15, 8))\n",
    "sns.violinplot(x = 'eueat', y = 'ertseat', data = data, inner='box', palette=c1, ax = ax)\n",
    "\n",
    "data = data.drop('eueat', axis = 1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Построим парные графики для всех признаков и увидим, что каких-то новых закономерностей не видно. \n",
    "\n",
    "Разве что интересно посмотреть на распределение ожирения и дохода. Видно, что больше всего людей с доходом более 185% от уровня бедности и без ожирения, немного меньше таких же, но с ожирением.\n",
    "У людей с доходом меньше 185% распределение с ожирением/без такое же."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sns.pairplot(data=data, hue='obese', palette='spring')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sns.set(font_scale=1, style=\"darkgrid\")\n",
    "sns_jointplot = sns.jointplot('obese', 'eeincome1', data=data, kind='kde', color=c, size=6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = data.reset_index().drop('index', axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Выбор метрики\n",
    "Перейдем к выбору метрики качества модели.\n",
    "\n",
    "У меня в исследовании задача бинарной классификации. Допустимые метрики качества:\n",
    "* Accuracy\n",
    "* Recall\n",
    "* Precision\n",
    "* F1-score\n",
    "* Logloss\n",
    "* Roc auc\n",
    "\n",
    "Первые три метрики простые и понятные, но имеют больше минусов, чем плюсов. Logloss чаще используется для оптимизации. А вот использовать пару f1-score и roc auc для оценки модели кажется хорошей идеей. Потому что если обе метрики высоки значит, и модель подобрана неплохо и в данных нет перекоса. А если одна из метрик высокая, а другая низкая, то, вероятно, следует поискать в модели ошибки."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Выбор модели\n",
    "\n",
    "Есть несколько подходящих для задачи бинарной классификации алгоритмов:\n",
    "*   KNeighborsClassifier()\n",
    "*   DecisionTreeClassifier()\n",
    "*   LinearSVC()\n",
    "*   LogisticRegression()\n",
    "*   RandomForestClassifier(), \n",
    "*   GradientBoostingClassifier()\n",
    "\n",
    "Попробуем их все на базовых настройках, потом выберем лучшую и будем рабоатть с ней дальше."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "# Предобработка данных\n",
    "\n",
    "Основную предобработку данных мы уже провели: заполнили пропуски, обработали выбросы, удалили ненужные признаки.\n",
    "\n",
    "Теперь приобразуем категориальные признаки с помощью dummy кодирования и приведем к виду разряженных матриц, а количественные данные скалируем.\n",
    "\n",
    "Разобьем данные на тренировочную, тестовую и валидационную выборки. Данных не очень много, поэтому на тест и валидацию пойдет по ~1000 строк. Утечки данных быть не может, так как нет временной оси, опросы проводились примерно в одно время, поэтому перемешивание данных оставляем True по дефолту."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.preprocessing import StandardScaler, OneHotEncoder, PolynomialFeatures\n",
    "from scipy.sparse import hstack\n",
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "X_train, X_test, y_train, y_test = train_test_split(data.drop('obese', axis=1), data['obese'], test_size=0.2, random_state=777)\n",
    "X_valid, X_test, y_valid, y_test = train_test_split(X_test, y_test, test_size=0.5, random_state=777)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data_scale_train = X_train[['ertpreat', 'ertseat', 'euhgt']]\n",
    "data_scale_test = X_test[['ertpreat', 'ertseat', 'euhgt']]\n",
    "scaler = StandardScaler()\n",
    "data_scaled_train = scaler.fit_transform(data_scale_train)\n",
    "data_scaled_test = scaler.transform(data_scale_test)\n",
    "\n",
    "data_dum_train = X_train.drop(X_train[['ertpreat', 'ertseat', 'euhgt']], axis = 1)\n",
    "data_dum_test = X_test.drop(X_test[['ertpreat', 'ertseat', 'euhgt']], axis = 1)\n",
    "\n",
    "ohe = OneHotEncoder()\n",
    "data_ohe_train = ohe.fit_transform(data_dum_train)\n",
    "data_ohe_test = ohe.transform(data_dum_test)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Кросс-валидация и настройка гиперпараметров модели"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train_sparse = hstack([data_scaled_train, data_ohe_train\\\n",
    "                                    ]).tocsr(copy = False) \n",
    "\n",
    "X_test_sparse = hstack([data_scaled_test, data_ohe_test\\\n",
    "                             ]).tocsr(copy = False) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.neighbors import KNeighborsClassifier\n",
    "from sklearn.tree import DecisionTreeClassifier\n",
    "from sklearn.svm import LinearSVC\n",
    "from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.model_selection import cross_val_score\n",
    "from time import time\n",
    "\n",
    "models = [\n",
    "    KNeighborsClassifier(),\n",
    "    DecisionTreeClassifier(random_state = 777),\n",
    "    LinearSVC(random_state = 777),\n",
    "    RandomForestClassifier(n_estimators=100, random_state = 777), \n",
    "    GradientBoostingClassifier(n_estimators=100, random_state = 777),\n",
    "    LogisticRegression(random_state = 777),\n",
    "]\n",
    "\n",
    "for model in models:\n",
    "    start = time()\n",
    "    model.fit(X_train_sparse, y_train)\n",
    "    score = np.mean(cross_val_score(model, X_train_sparse, y_train, cv=9, scoring='roc_auc'))\n",
    "    scoref1 = np.mean(cross_val_score(model, X_train_sparse, y_train, cv=9, scoring='f1'))\n",
    "    end = time()\n",
    "    ste = end - start\n",
    "    print('roc auc:', score, '\\n', 'f1:', scoref1, '\\n', 'time:', ste, '\\n', model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "Дальше стоит работать с LinearSVC, GradientBoostingClassifier и LogisticRegression, так как у них самый хороший скор на этих данных.\n",
    "\n",
    "# Создание новых признаков и описание этого процесса\n",
    "\n",
    "Добавим новые признаки. В основном, у меня только категориальные признаки и немного численных. Поэтому в таких случаях часто используют cross interaction features. Поэтому, добавим их. Сделаем их второго уровня,  без полиномиальных признаков и без столбика смещения."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "poly = PolynomialFeatures(degree=2, interaction_only=True, include_bias=False)\n",
    "data_poly_train = poly.fit_transform(X_train)\n",
    "data_poly_test = poly.transform(X_test)\n",
    "data_poly_train = pd.DataFrame(data_poly_train)\n",
    "data_poly_test = pd.DataFrame(data_poly_test)\n",
    "\n",
    "scaler = StandardScaler()\n",
    "data_poly_sacaled_train = scaler.fit_transform(data_poly_train)\n",
    "data_poly_scaled_test = scaler.transform(data_poly_test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train_sparse = hstack([data_scaled_train, data_ohe_train,\\\n",
    "                                    data_poly_sacaled_train]).tocsr(copy = False) \n",
    "\n",
    "X_test_sparse = hstack([data_scaled_test, data_ohe_test,\\\n",
    "                             data_poly_scaled_test]).tocsr(copy = False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "models = [\n",
    "    LinearSVC(random_state = 777),\n",
    "    GradientBoostingClassifier(n_estimators=100, random_state = 777),\n",
    "    LogisticRegression(random_state = 777)\n",
    "]\n",
    "\n",
    "for model in models:\n",
    "    start = time()\n",
    "    model.fit(X_train_sparse, y_train)\n",
    "    score = np.mean(cross_val_score(model, X_train_sparse, y_train, cv=9, scoring='roc_auc'))\n",
    "    scoref1 = np.mean(cross_val_score(model, X_train_sparse, y_train, cv=9, scoring='f1'))\n",
    "    end = time()\n",
    "    ste = end - start\n",
    "    print('roc auc:', score, '\\n', 'f1:', scoref1, '\\n', 'time:', ste, '\\n', model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Качество почти не выросло, зато времени на обучение стало уходить намного больше.\n",
    "\n",
    "Исходя из первого эксперимента, можно устновить, что LinearSVC лучше всего справляется, но у LogisticRegression качество почти такое, а времени уходит гораздо меньше. Поэтому, целесообразнее оставить ее. \n",
    "\n",
    "# Настройка гиперпараметров модели"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train_sparse = hstack([data_scaled_train, data_ohe_train]).tocsr(copy = False)\n",
    "\n",
    "X_test_sparse = hstack([data_scaled_test, data_ohe_test]).tocsr(copy = False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for i in ['liblinear', 'sag', 'saga']:\n",
    "    for j in [100, 1000, 5000, 10000]:\n",
    "        for k in [0.1, 1]:\n",
    "                start = time()\n",
    "                model = LogisticRegression(solver=i, max_iter=j, C=k, random_state=777)\n",
    "                model.fit(X_train_sparse, y_train)\n",
    "                score = np.mean(cross_val_score(model, X_train_sparse, y_train, cv=9, scoring='roc_auc'))\n",
    "                scoref1 = np.mean(cross_val_score(model, X_train_sparse, y_train, cv=9, scoring='f1'))\n",
    "                end = time()\n",
    "                ste = end - start\n",
    "                print('solver:', i, '\\n', 'max_iter:', j, '\\n', 'C:', k, '\\n',\\\n",
    "                      'roc auc:', score, '\\n', 'f1:', scoref1, '\\n', 'time:', ste)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Лучше всего себя показывает по времени и качеству solver = liblinear, max_iter = 10000, C = 1.\n",
    "Их оставим и проверим на тестовой выборке.\n",
    "\n",
    "# Валидация на тестовой выборке"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data_scale_train = X_train[['ertpreat', 'ertseat', 'euhgt']]\n",
    "data_scale_test = X_test[['ertpreat', 'ertseat', 'euhgt']]\n",
    "scaler = StandardScaler()\n",
    "data_scaled_train = scaler.fit_transform(data_scale_train)\n",
    "data_scaled_test = scaler.transform(data_scale_test)\n",
    "\n",
    "data_dum_train = X_train.drop(X_train[['ertpreat', 'ertseat', 'euhgt']], axis = 1)\n",
    "data_dum_test = X_test.drop(X_test[['ertpreat', 'ertseat', 'euhgt']], axis = 1)\n",
    "\n",
    "ohe = OneHotEncoder()\n",
    "data_ohe_train = ohe.fit_transform(data_dum_train)\n",
    "data_ohe_test = ohe.transform(data_dum_test)\n",
    "\n",
    "X_train_sparse = hstack([data_scaled_train, data_ohe_train\\\n",
    "                                    ]).tocsr(copy = False) \n",
    "\n",
    "X_test_sparse = hstack([data_scaled_test, data_ohe_test\\\n",
    "                             ]).tocsr(copy = False) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.metrics import f1_score, roc_auc_score\n",
    "\n",
    "for i in ['liblinear']:\n",
    "    for j in [10000]:\n",
    "        for k in [1]:\n",
    "                start = time()\n",
    "                model = LogisticRegression(solver=i, max_iter=j, C=k, random_state=777)\n",
    "                model.fit(X_train_sparse, y_train)\n",
    "                preds = model.predict(X_test_sparse)\n",
    "                score = roc_auc_score(y_test, preds)\n",
    "                scoref1 = f1_score(y_test, preds)\n",
    "                end = time()\n",
    "                ste = end - start\n",
    "                print('solver:', i, '\\n', 'max_iter:', j, '\\n', 'C:', k, '\\n',\\\n",
    "                      'roc auc:', score, '\\n', 'f1:', scoref1, '\\n', 'time:', ste)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "Значение не сильно изменилось для тестовой выборки и падение было ожидаемым.\n",
    "Значит, можно обучать модель на всех тренировочных и тестовых данных и делать предсказание на валидационной выборке.\n",
    "\n",
    "# Прогноз для валидационной выборки"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train = X_train.append(X_test).reset_index().drop('index', axis=1)\n",
    "y_train = y_train.append(y_test)\n",
    "\n",
    "data_scale_train = X_train[['ertpreat', 'ertseat', 'euhgt']]\n",
    "data_scale_test = X_valid[['ertpreat', 'ertseat', 'euhgt']]\n",
    "scaler = StandardScaler()\n",
    "data_scaled_train = scaler.fit_transform(data_scale_train)\n",
    "data_scaled_test = scaler.transform(data_scale_test)\n",
    "\n",
    "data_dum_train = X_train.drop(X_train[['ertpreat', 'ertseat', 'euhgt']], axis = 1)\n",
    "data_dum_test = X_valid.drop(X_valid[['ertpreat', 'ertseat', 'euhgt']], axis = 1)\n",
    "\n",
    "ohe = OneHotEncoder()\n",
    "data_ohe_train = ohe.fit_transform(data_dum_train)\n",
    "data_ohe_test = ohe.transform(data_dum_test)\n",
    "\n",
    "X_train_sparse = hstack([data_scaled_train, data_ohe_train\\\n",
    "                                    ]).tocsr(copy = False) \n",
    "\n",
    "X_test_sparse = hstack([data_scaled_test, data_ohe_test\\\n",
    "                             ]).tocsr(copy = False) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.metrics import f1_score, roc_auc_score\n",
    "\n",
    "for i in ['liblinear']:\n",
    "    for j in [10000]:\n",
    "        for k in [1]:\n",
    "                start = time()\n",
    "                model = LogisticRegression(solver=i, max_iter=j, C=k, random_state=777)\n",
    "                model.fit(X_train_sparse, y_train)\n",
    "                preds = model.predict(X_test_sparse)\n",
    "                score = roc_auc_score(y_valid, preds)\n",
    "                scoref1 = f1_score(y_valid, preds)\n",
    "                end = time()\n",
    "                ste = end - start\n",
    "                print('solver:', i, '\\n', 'max_iter:', j, '\\n', 'C:', k, '\\n',\\\n",
    "                      'roc auc:', score, '\\n', 'f1:', scoref1, '\\n', 'time:', ste)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Опять же, качество не сильно отличается от тренировочного, а это значит, что модель хорошо обрабатывает новые данные."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Выводы\n",
    "\n",
    "В целом, скор не очень высокий. Похоже, это из-за того, что данные весьма специфичные, много пропусков. Но все равно, оказывается, что по поведению домохозяйств можно предсказывать, будет ли ожирение у членов этой семьи!\n",
    "\n",
    "Возможные улучшения: \n",
    "* Собирать другие данные о людях, более полные и чистые (группа крови, пол, перечень продуктов за неделю, болезни и прочее).\n",
    "* Все же поделить людей по имт на здоровых, с излишним весом и с ожирением и решить задачу многоклассовой классификации.\n",
    "* Не удалять пропуски так явно, как это сделала я, а избрать другой способ обработки.\n",
    "\n",
    "\n",
    "Спасибо за внимание, прошу быть не очень строгими судьями :)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
